/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkHttpMediaUt.h"
#include "MkEvent/MkEventPool.h"
#include "MkEvent/MkEvent.h"
#include "MkEvent/MkTimer.h"
#include "MkUtil/MkUtil.h"
#include "MkHttpMedia/MkHttpMediaServer.h"
#include "MkHttpMedia/MkWsFlvPlayer.h"
#include "MkHttp/MkHttpUtil.h"
#include "MkUtil/MkLog.h"
#include "MkMedia/MkFileSource.h"
#include "MkHttp/MkHttpSession.h"
#include "MkEvent/MkTimerPool.h"

void CMkHttpMediaUt::SetUp()
{
    m_pHttpMediaServer = nullptr;
    m_pEventPool = nullptr;
    m_pTimer = nullptr;
    m_pH264FileSource = nullptr;
    m_pH265FileSource = nullptr;
    m_pAacFileSource = nullptr;
    m_pWsPlayer = nullptr;
    m_pMediaConsume = nullptr;
    m_pClient = nullptr;
    m_bClientConnected = FALSE;

    m_bClientConnected = FALSE;
    m_pEventPool = new CMkEventPool;
    m_pEventPool->Start(1);
    m_pTimer = new CMkTimer;
    m_pTimer->StartTimerThread();
    MkString H264FileName = MkString(MkUtSourcePath) + "/test.264";
    MkString H265FileName = MkString(MkUtSourcePath) + "/test.265";
    MkString AacFileName = MkString(MkUtSourcePath) + "/test.aac";
    FILE *fp264 = fopen(H264FileName.c_str(), "rb");
    FILE *fp265 = fopen(H265FileName.c_str(), "rb");
    FILE *fpAac = fopen(AacFileName.c_str(), "rb");

    m_pH264FileSource = new CMkH264FileSource(fp264, nullptr);
    m_pH265FileSource = new CMkH265FileSource(fp265, nullptr);
    m_pAacFileSource = new CMkAacFileSource(fpAac, nullptr);
    m_WaitHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);

    Uint32 ErrorCode = m_pH264FileSource->StartMediaSource(nullptr, nullptr);
    EXPECT_EQ(NoneError, ErrorCode);
    ErrorCode = m_pH265FileSource->StartMediaSource(nullptr, nullptr);
    EXPECT_EQ(NoneError, ErrorCode);
    ErrorCode = m_pAacFileSource->StartMediaSource(nullptr, nullptr);
    EXPECT_EQ(NoneError, ErrorCode);

    ErrorCode = m_pH264FileSource->GetNextFrame(m_VecPacket264);
    EXPECT_EQ(NoneError, ErrorCode);
    EXPECT_EQ(FALSE, m_VecPacket264.empty());

    ErrorCode = m_pH265FileSource->GetNextFrame(m_VecPacket265);
    EXPECT_EQ(NoneError, ErrorCode);
    EXPECT_EQ(FALSE, m_VecPacket265.empty());

    ErrorCode = m_pAacFileSource->GetNextFrame(m_VecPacketAac);
    EXPECT_EQ(NoneError, ErrorCode);
    EXPECT_EQ(FALSE, m_VecPacketAac.empty());

    MkMediaCallbacks MediaCbs;
    MediaCbs.GetSourceParamCb = [this](const MkString& Path, CMkVideoParam& VideoParam, CMkAudioParam& AudioParam, Uint32& Duration, Uint32& FileSize, MkString& SourceSession) {
        if ("/live/test264aac_play" == Path) {
            VideoParam = m_pH264FileSource->GetVideoParam();
            AudioParam = m_pAacFileSource->GetAudioParam();
            Duration = 1;
            FileSize = 100;
        } else if ("/live/test265aac_play" == Path) {
            VideoParam = m_pH265FileSource->GetVideoParam();
            AudioParam = m_pAacFileSource->GetAudioParam();
            Duration = 1;
            FileSize = 100;
        } else if ("/live/test264_play" == Path) {
            VideoParam = m_pH264FileSource->GetVideoParam();
            AudioParam = CMkAudioParam();
            Duration = 1;
            FileSize = 100;
        } else if ("/live/test265_play" == Path) {
            VideoParam = m_pH265FileSource->GetVideoParam();
            AudioParam = CMkAudioParam();
            Duration = 1;
            FileSize = 100;
        } else if ("/live/testAac_play" == Path) {
            VideoParam = CMkVideoParam();
            AudioParam = m_pAacFileSource->GetAudioParam();
            Duration = 1;
            FileSize = 100;
        } else {
            return SystemError;
        }
        SourceSession = Path;
        return NoneError;
    };

    MediaCbs.ConsumerAddCb = [this](const MkString& SourceSession, CMkMediaConsume* pConsume, CMkMediaConsumer* pConsumer) {
        m_pMediaConsume = pConsume;
        pConsume->AddConsumer(pConsumer);
        Uint8 pBuf[Len1K] = { 0 };
        CMkMediaSource MediaSource(MkMediaSourceFileFlv);
        if ("/live/test264aac_play" == SourceSession) {
            MediaSource.SetVideoParam(m_pH264FileSource->GetVideoParam());
            MediaSource.SetAudioParam(m_pAacFileSource->GetAudioParam());
            MediaSource.AddCacheEsPacket(*m_VecPacket264.begin());
            MediaSource.AddCacheEsPacket(*m_VecPacketAac.begin());
            pConsume->ConsumeEsPacket(&MediaSource, *m_VecPacket264.begin(), pBuf, Len1K);
            pConsume->ConsumeEsPacket(&MediaSource, *m_VecPacketAac.begin(), pBuf, Len1K);
            MkDebugLog("send h264 aac stream\n");
        } else if ("/live/test265aac_play" == SourceSession) {
            MediaSource.SetVideoParam(m_pH265FileSource->GetVideoParam());
            MediaSource.SetAudioParam(m_pAacFileSource->GetAudioParam());
            MediaSource.AddCacheEsPacket(*m_VecPacket265.begin());
            MediaSource.AddCacheEsPacket(*m_VecPacketAac.begin());
            pConsume->ConsumeEsPacket(&MediaSource, *m_VecPacket265.begin(), pBuf, Len1K);
            pConsume->ConsumeEsPacket(&MediaSource, *m_VecPacketAac.begin(), pBuf, Len1K);
            MkDebugLog("send h265 aac stream\n");
        } else if ("/live/test264_play" == SourceSession) {
            pConsume->ConsumeEsPacket(m_pH264FileSource, *m_VecPacket264.begin(), pBuf, Len1K);
            MkDebugLog("send h264 stream\n");
        } else if ("/live/test265_play" == SourceSession) {
            pConsume->ConsumeEsPacket(m_pH265FileSource, *m_VecPacket265.begin(), pBuf, Len1K);
            MkDebugLog("send h265 stream\n");
        } else if ("/live/testAac_play" == SourceSession) {
            pConsume->ConsumeEsPacket(m_pAacFileSource, *m_VecPacketAac.begin(), pBuf, Len1K);
            MkDebugLog("send aac stream\n");
        }
        return NoneError;
    };
    MediaCbs.SourceAddCb = [](CMkMediaSource* pSource) {
        return NoneError;
    };
    MediaCbs.ConsumerClosedCb = [this](const MkString& SourceSession, const MkMediaConsumeType& ConsumeType, const MkString& ConsumerSession) {
        if (m_pMediaConsume) {
            m_pMediaConsume->DeleteConsumer(ConsumerSession);

        }
        return NoneError;
    };
    MediaCbs.SourceDeleteCb = [](const MkString& SourceSession) {
        return NoneError;
    };

    MediaCbs.ConsumerPlayCtrlCb = [](CMkMediaConsumer* pConsumer, const MkPlaybackControlType& ControlType, float fSpeed, Uint32 offsetSec) {
        return NoneError;
    };

    m_pHttpMediaServer = new CMkHttpMediaServer(m_pEventPool, m_pTimer, [this](const MkHttpMethod& Method, const MkString& Path, const MkMapString& MapQuery, const Json::Value& JsonReq, const MkHttpResponseJsonCb& cb) {
        cb(NoneError, Json::Value());
        return NoneError;
    }, [this](CMkHttpSession* pSession) {
        pSession->SendWebSocketText("112");
    }, MediaCbs);

    while (1) {
        m_HttpMediaPort = CMkUtil::GetRandomNumber() % 0xFFFF;
        if (NoneError == m_pHttpMediaServer->StartServer(m_HttpMediaPort)) {
            break;
        }
    }
}

void CMkHttpMediaUt::TearDown()
{
    CMkThread::ThreadSleep(100);
    m_pTimer->StopTimerThread();
    m_pEventPool->Stop();
    MkDelete(m_pHttpMediaServer);
    MkDelete(m_pTimer);
    MkDelete(m_pEventPool);
    MkDelete(m_pH264FileSource);
    MkDelete(m_pH265FileSource);
    MkDelete(m_pAacFileSource);
    CloseHandle(m_WaitHandle);
    MkDelete(m_pMediaConsume);
}

TEST_F(CMkHttpMediaUt, h264AacTest)
{
    m_pWsPlayer = new CMkWsFlvPlayer(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_pWsPlayer = nullptr;
        SetEvent(m_WaitHandle);
        MkDebugLog("h264 aac closed\n");
    });
    m_pWsPlayer->StartMediaSource("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + "/live/test264aac_play.flv", [this](CMkMediaSource* pMediaSource, Uint32 ErrorCode) {
        EXPECT_EQ(NoneError, ErrorCode);
        m_bClientConnected = TRUE;
        Json::Value JsonReq;
        JsonReq["controlType"] = "SetSpeed";
        JsonReq["speed"] = 2;
        JsonReq["offsetSec"] = 0;
        m_pWsPlayer->SendWebSocketMessage(JsonReq.toStyledString());
        return NoneError;
    }, [this](const MkEsPacket& EsPacket, const CMkVideoParam& VideoParam, const CMkAudioParam& AudioParam) {
        MkDebugLog("recv 264 aac stream\n");
        EXPECT_EQ(VideoParam.GetCodec(), MkCodecH264);
        EXPECT_EQ(AudioParam.GetCodec(), MkCodecAac);
        if (m_pWsPlayer) {
            EXPECT_EQ(m_pWsPlayer->GetVideoParam().GetCodec(), MkCodecH264);
            EXPECT_EQ(m_pWsPlayer->GetAudioParam().GetCodec(), MkCodecAac);
            m_pWsPlayer->StopMediaSource();
        }
        return NoneError;
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, h265AacTest)
{
    m_pWsPlayer = new CMkWsFlvPlayer(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_pWsPlayer = nullptr;
        SetEvent(m_WaitHandle);
        MkDebugLog("h265 aac closed\n");
    });
    m_pWsPlayer->StartMediaSource("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + "/live/test265aac_play.flv", [this](CMkMediaSource* pMediaSource, Uint32 ErrorCode) {
        EXPECT_EQ(NoneError, ErrorCode);
        m_bClientConnected = TRUE;
        Json::Value JsonReq;
        JsonReq["controlType"] = "SetSpeed";
        JsonReq["speed"] = 2;
        JsonReq["offsetSec"] = 0;
        m_pWsPlayer->SendWebSocketMessage(JsonReq.toStyledString());
        return NoneError;
    }, [this](const MkEsPacket& EsPacket, const CMkVideoParam& VideoParam, const CMkAudioParam& AudioParam) {
        MkDebugLog("recv 265 aac stream\n");
        EXPECT_EQ(VideoParam.GetCodec(), MkCodecH265);
        EXPECT_EQ(AudioParam.GetCodec(), MkCodecAac);
        if (m_pWsPlayer) {
            EXPECT_EQ(m_pWsPlayer->GetVideoParam().GetCodec(), MkCodecH265);
            EXPECT_EQ(m_pWsPlayer->GetAudioParam().GetCodec(), MkCodecAac);
            m_pWsPlayer->StopMediaSource();
        }
        return NoneError;
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, h265Test)
{
    m_pWsPlayer = new CMkWsFlvPlayer(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_pWsPlayer = nullptr;
        SetEvent(m_WaitHandle);
    });
    m_pWsPlayer->StartMediaSource("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + "/live/test265_play.flv", [this](CMkMediaSource* pMediaSource, Uint32 ErrorCode) {
        EXPECT_EQ(NoneError, ErrorCode);
        m_bClientConnected = TRUE;
        Json::Value JsonReq;
        JsonReq["controlType"] = "SetSpeed";
        JsonReq["speed"] = 2;
        JsonReq["offsetSec"] = 0;
        m_pWsPlayer->SendWebSocketMessage(JsonReq.toStyledString());
        return NoneError;
    }, [this](const MkEsPacket& EsPacket, const CMkVideoParam& VideoParam, const CMkAudioParam& AudioParam) {
        EXPECT_EQ(VideoParam.GetCodec(), MkCodecH265);
        EXPECT_EQ(AudioParam.GetCodec(), MkCodecUnknown);
        if (m_pWsPlayer) {
            EXPECT_EQ(m_pWsPlayer->GetVideoParam().GetCodec(), MkCodecH265);
            EXPECT_EQ(m_pWsPlayer->GetAudioParam().GetCodec(), MkCodecUnknown);
            m_pWsPlayer->StopMediaSource();
        }
        return NoneError;
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, h264Test)
{
    m_pWsPlayer = new CMkWsFlvPlayer(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_pWsPlayer = nullptr;
        SetEvent(m_WaitHandle);
    });
    m_pWsPlayer->StartMediaSource("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + "/live/test264_play.flv", [this](CMkMediaSource* pMediaSource, Uint32 ErrorCode) {
        EXPECT_EQ(NoneError, ErrorCode);
        m_bClientConnected = TRUE;
        Json::Value JsonReq;
        JsonReq["controlType"] = "SetSpeed";
        JsonReq["speed"] = 2;
        JsonReq["offsetSec"] = 0;
        m_pWsPlayer->SendWebSocketMessage(JsonReq.toStyledString());
        return NoneError;
    }, [this](const MkEsPacket& EsPacket, const CMkVideoParam& VideoParam, const CMkAudioParam& AudioParam) {
        EXPECT_EQ(VideoParam.GetCodec(), MkCodecH264);
        EXPECT_EQ(AudioParam.GetCodec(), MkCodecUnknown);
        if (m_pWsPlayer) {
            EXPECT_EQ(m_pWsPlayer->GetVideoParam().GetCodec(), MkCodecH264);
            EXPECT_EQ(m_pWsPlayer->GetAudioParam().GetCodec(), MkCodecUnknown);
            m_pWsPlayer->StopMediaSource();
        }
        return NoneError;
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, AacTest)
{
    m_pWsPlayer = new CMkWsFlvPlayer(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_pWsPlayer = nullptr;
        SetEvent(m_WaitHandle);
    });
    m_pWsPlayer->StartMediaSource("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + "/live/testAac_play.flv", [this](CMkMediaSource* pMediaSource, Uint32 ErrorCode) {
        EXPECT_EQ(NoneError, ErrorCode);
        m_bClientConnected = TRUE;
        Json::Value JsonReq;
        JsonReq["controlType"] = "SetSpeed";
        JsonReq["speed"] = 2;
        JsonReq["offsetSec"] = 0;
        m_pWsPlayer->SendWebSocketMessage(JsonReq.toStyledString());
        return NoneError;
    }, [this](const MkEsPacket& EsPacket, const CMkVideoParam& VideoParam, const CMkAudioParam& AudioParam) {
        EXPECT_EQ(VideoParam.GetCodec(), MkCodecUnknown);
        EXPECT_EQ(AudioParam.GetCodec(), MkCodecAac);
        if (m_pWsPlayer) {
            EXPECT_EQ(m_pWsPlayer->GetVideoParam().GetCodec(), MkCodecUnknown);
            EXPECT_EQ(m_pWsPlayer->GetAudioParam().GetCodec(), MkCodecAac);
            m_pWsPlayer->StopMediaSource();
        }
        return NoneError;
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, HttpTest)
{
    m_pClient = new CMkHttpClient(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_ClienkLock.Lock();
        m_pClient = nullptr;
        m_ClienkLock.Unlock();
        SetEvent(m_WaitHandle);
    });
    m_pClient->SendRequest(MkHttpMethodGet, "http://127.0.0.1:" + std::to_string(m_HttpMediaPort) + m_pHttpMediaServer->GetApiUriPrefix() + "test", MkMapString(), "", MkMapString(), [this](const MkHttpStatusCode& StatusCode, const MkString& Res) {
        EXPECT_EQ(StatusCode, MkHttpStateOk);
        m_pClient->Close();
    });
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}

TEST_F(CMkHttpMediaUt, WsTest)
{
    m_pClient = new CMkHttpClient(m_pEventPool->GetFreeEvent(), m_pTimer, [this](CMkTcpClient* pClient) {
        pClient->DeleteLater();
        m_ClienkLock.Lock();
        m_pClient = nullptr;
        m_ClienkLock.Unlock();
        SetEvent(m_WaitHandle);
    });
    m_pClient->SetSendPingFlag(TRUE);
    m_pClient->WebSocketConnect("ws://127.0.0.1:" + std::to_string(m_HttpMediaPort) + m_pHttpMediaServer->GetWebSocketPath(),
        [this]() {
        if (m_pClient) {
            m_pClient->SendWebSocketMessage(m_pHttpMediaServer->GetWebSocketAccessKey());
        }
    }, [this](const Uint8* pBuf, Uint32 nLen) {
        if (m_pClient) {
            m_pClient->Close();
        }
    }, []() {});
    WaitForSingleObject(m_WaitHandle, 0xFFFF);
}